﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Linq;
using LAIR.ResourceAPIs.WordNet;

namespace CBTM.IO.Miscellanous
{
    public struct Paths
    {
        // cơ sở dữ liệu WordNet 3.1
        public const string WNDatabase = "WNDB";

        // tài nguyên chương trình
        public const string MRD_EVDict = "Resources/EVDict.xml";
        public const string MRD_EVDictG_old = "Resources/EVDict_G_old.xml";
        public const string MRD_EVDictG_new = "Resources/EVDict_G_new.xml";
        public const string CoreWN = "Resources/core-wordnet.noun";
        public const string SW_En = "Resources/stopwords.en";
        public const string SW_Vi = "Resources/stopwords.vi";
        public const string SenseIndex = "Resources/index.sense";

        // các tập tin được tạo ra trong quá trình chạy chương trình
        public const string WNIndex = "Misc/wnindex.txt";
        public const string CCDic = "Misc/ccdic.txt";
        public const string CODic = "Misc/codic.txt";
        public const string BSTCheck = "Misc/bst-check.txt";
        public const string FreqUni = "Resources/freq-unigram.txt";
        public const string FreqBi = "Resources/freq-bigram.txt";

        // bảng
        public const string TTable1 = "Tables/TTable1.xml";
        public const string TTable2 = "Tables/TTable2.xml";
        public const string TTable3 = "Tables/TTable3.xml";
        public const string SCTable = "Tables/SCTable.xml";
        public const string BSTTable1 = "Tables/BSTTable1.xml";
        public const string BSTTable2 = "Tables/BSTTable2.xml";
        public const string BSTTable3 = "Tables/BSTTable3.xml";
        public const string VWNTable = "Tables/VWNTable.xml";
    }

    public static class Misc
    {
        static WordNetEngine _wnengine = new WordNetEngine(Paths.WNDatabase, false);

        /// <summary>
        /// Duyệt theo chiều sâu, dùng cho quá trình tìm hyponym của class code
        /// </summary>
        /// <param name="synset"></param>
        /// <param name="classcodes"></param>
        /// <param name="writer"></param>
        public static void Traverse(SynSet synset, List<int> classcodes, StreamWriter writer)
        {
            foreach (SynSet s in synset.GetRelatedSynSets(WordNetEngine.SynSetRelation.Hyponym, false))
                // chỉ chọn các hyponym không phải class code
                if (!classcodes.Contains(s.Offset))
                {
                    writer.Write(s.Offset + ";");
                    // đi tới các hyponym của hyponym đang xét
                    Traverse(s, classcodes, writer);
                }
        }

        /// <summary>
        /// Trả về class code của synset
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="sctable"></param>
        /// <returns></returns>
        public static string GetClassCode(string offset, Dictionary<string, string> sctable)
        {
            string classcode = "";
            foreach (var kvp in sctable)
                if (kvp.Value.Contains(offset))
                    return kvp.Key;
            return classcode;
        }

        /// <summary>
        /// Tạo danh sách stop word
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        public static List<string> LoadStopWords(string file)
        {
            List<string> list = new List<string>();
            StreamReader sr = new StreamReader(file);
            while (sr.Peek() >= 0)
                list.Add(sr.ReadLine());
            sr.Close();
            Console.WriteLine("Stop word list loaded.");
            return list;
        }

        /// <summary>
        /// Trả về unigram và bigram chồng của từ tiếng Việt - dùng để tính xác suất ngram
        /// </summary>
        /// <param name="sctable"></param>
        /// <param name="stoplist"></param>
        /// <param name="p"></param>
        /// <param name="pug"></param>
        /// <param name="pbg"></param>
        public static void GetFreqList(Dictionary<string, string> sctable, List<string> stoplist, Dictionary<string, float> p, out Dictionary<string, Dictionary<string, float>> pug, out Dictionary<string, Dictionary<string, float>> pbg)
        {
            // lập danh sách unigram và bigram
            pug = new Dictionary<string, Dictionary<string, float>>();//unigram
            pbg = new Dictionary<string, Dictionary<string, float>>();//bigram chồng
            float utotal = 0, btotal = 0;// đếm số lượng unigram và bigram
            foreach (var kvp in p)
            {
                string[] args = kvp.Key.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
                string g = GetClassCode(args[2], sctable);
                if (g == "") continue;
                string[] unigrams = args[0].Split('_');
                utotal += unigrams.Length;
                btotal += unigrams.Length - 1;
                Dictionary<string, float> ufreqlist;
                Dictionary<string, float> bfreqlist;
                // cộng dồn unigram và bigram đối với các lớp ngữ nghĩa đã tồn tại
                if (pug.TryGetValue(g, out ufreqlist))
                {
                    bfreqlist = pbg[g];
                    int count = 0;
                    string pre_u = "";
                    foreach (string u in unigrams)
                    {
                        float val;
                        if (!stoplist.Contains(u))
                        {
                            if (ufreqlist.TryGetValue(u, out val))
                                ufreqlist[u] = val + 1;
                            else
                                ufreqlist.Add(u, 1);
                        }
                        if (count > 0)
                        {
                            string b = pre_u + '_' + u;
                            if (bfreqlist.TryGetValue(b, out val))
                                bfreqlist[b] = val + 1;
                            else
                                bfreqlist.Add(b, 1);
                        }
                        pre_u = u;
                        count++;
                    }
                    pug[g] = ufreqlist;
                    pbg[g] = bfreqlist;
                }
                // thêm lớp ngữ nghĩa mới
                else
                {
                    ufreqlist = new Dictionary<string, float>();
                    bfreqlist = new Dictionary<string, float>();
                    string pre_u = "";
                    int count = 0;
                    foreach (string u in unigrams)
                    {
                        float val;
                        if (!stoplist.Contains(u))
                        {
                            if (ufreqlist.TryGetValue(u, out val))
                                ufreqlist[u] += 1;
                            else
                                ufreqlist.Add(u, 1);
                        }
                        if (count > 0)
                        {
                            string b = pre_u + '_' + u;
                            if (bfreqlist.TryGetValue(b, out val))
                                bfreqlist[b] += 1;
                            else
                                bfreqlist.Add(b, 1);
                        }
                        pre_u = u;
                        count++;

                    }
                    pug.Add(g, ufreqlist);
                    pbg.Add(g, bfreqlist);
                }
            }

            List<string> gkeys;
            gkeys = new List<string>(pug.Keys);
            foreach (string g in gkeys)
            {
                List<string> ukeys = new List<string>(pug[g].Keys);
                foreach (string u in ukeys)
                    pug[g][u] /= utotal;
            }
            gkeys = new List<string>(pbg.Keys);
            foreach (string g in gkeys)
            {
                List<string> bkeys = new List<string>(pbg[g].Keys);
                foreach (string b in bkeys)
                    pbg[g][b] /= btotal;
            }
        }

        /// <summary>
        /// Kiểm tra tính hội tụ của kết quả
        /// </summary>
        /// <param name="pre_plist"></param>
        /// <param name="plist"></param>
        /// <param name="checkfile"></param>
        /// <param name="threshold"></param>
        /// <param name="converge"></param>
        /// <returns></returns>
        public static Dictionary<string, float> Review(Dictionary<string, float> pre_plist, Dictionary<string, float> plist, string checkfile, float threshold, out bool converge)
        {
            Dictionary<string, float> new_plist = new Dictionary<string, float>();
            converge = true;
            StreamWriter sw = File.AppendText(checkfile);
            sw.WriteLine("----- REMAINING -----");
            foreach (var key in plist.Keys)
            {
                float diff = plist[key] - pre_plist[key];
                if (diff < -threshold || diff > threshold)
                {
                    converge = false;
                    sw.WriteLine(key);
                }
                new_plist.Add(key, plist[key] - (diff / 2));
            }
            sw.WriteLine();
            sw.Close();
            return new_plist;
        }

        /// <summary>
        /// Kiểm tra số nghĩa tiếng Việt của từ tiếng Anh (để chọn ví dụ cho báo cáo)
        /// </summary>
        /// <param name="file"></param>
        private static void CheckMaxTranslations(string file)
        {
            int max = 0;
            string en = "";
            XElement root = XElement.Load(file);
            foreach (XElement e in root.Elements())
            {
                List<string> list = new List<string>();
                foreach (XElement ee in e.Elements())
                {
                    string[] arg = ee.Value.Split(':');
                    if (!list.Contains(arg[0]))
                        list.Add(arg[0]);
                }
                if (list.Count >= max)
                {
                    max = list.Count;
                    en = e.FirstAttribute.Value;
                }
            }
            Console.WriteLine(en);
            Console.WriteLine(max);
        }
    }
}
